/*
 * Licensed Materials - Property of tenxcloud.com
 * (C) Copyright 2018 TenxCloud. All Rights Reserved.
 * 2018-06-13  @author lizhen
 */

package crypto

import (
	"crypto/cipher"
	"crypto/des"
	"crypto/md5"
)

func NewDESCryptor(c config) *desEDE3CBC {
	k, iv := bytesToKey(md5.New, c.Secret(), []byte{}, 1, 24, 8)
	return &desEDE3CBC{
		key: k,
		iv:  iv,
	}
}

type desEDE3CBC struct {
	key []byte
	iv  []byte
}

func (d desEDE3CBC) Encrypt(content []byte) (encrypted []byte, err error) {
	var block cipher.Block
	if block, err = des.NewTripleDESCipher(d.key); err != nil {
		return
	}
	cbc := cipher.NewCBCEncrypter(block, d.iv)
	encrypted, err = d.crypt(content, cbc)
	return
}

func (d desEDE3CBC) Decrypt(encrypted []byte) (content []byte, err error) {
	var block cipher.Block
	if block, err = des.NewTripleDESCipher(d.key); err != nil {
		return
	}
	cbc := cipher.NewCBCDecrypter(block, d.iv)
	content, err = d.crypt(encrypted, cbc)
	return
}

func (d desEDE3CBC) crypt(in []byte, cbc cipher.BlockMode) (out []byte, err error) {
	blockSize := cbc.BlockSize()
	padded := padding(in, blockSize)
	entriesCount := len(padded)
	crypted := make([][]byte, entriesCount)
	for i := 0; i < entriesCount; i++ {
		crypted[i] = make([]byte, blockSize)
		cbc.CryptBlocks(crypted[i], padded[i])
	}
	contentSize := len(in)
	out = make([]byte, contentSize)
	for i := 0; i < entriesCount; i++ {
		for j := 0; j < blockSize; j++ {
			index := i*blockSize + j
			if index >= contentSize {
				break
			}
			out[index] = crypted[i][j]
		}
	}
	return
}

func padding(content []byte, blockSize int) (padded [][]byte) {
	contentLength := len(content)
	size := contentLength / blockSize
	if contentLength%blockSize != 0 {
		size++
	}
	padded = make([][]byte, size)
	for i := 0; i < size; i++ {
		padded[i] = make([]byte, blockSize)
		entry := padded[i]
		for j := 0; j < blockSize; j++ {
			index := i*blockSize + j
			if index >= contentLength {
				for k := j; k < blockSize; k++ {
					entry[k] = 0
				}
				break
			}
			entry[j] = content[index]
		}
	}
	return
}
